import { defineStore } from 'pinia'
import { post } from '@/utils/request' // 引入你封装的请求工具
import { ElMessage } from 'element-plus'
import type { RequestConfig } from '@/utils/request'

// 定义刷新令牌接口的响应类型
interface RefreshTokenResponse {
  accessToken: string // 后端返回的新访问令牌
}

// 创建 Pinia Store（使用 TypeScript 确保类型安全）
//注意以下我声明store的模板是使用defineStore函数来声明的
// 第一个参数是store的id，第二个参数是store的配置对象
export const useAuthStore = defineStore('auth', {
  //state 是store的状态 我可以在state中定义一些变量 这些变量就是store的状态
  // state 是存储应用共享数据的地方，
  // 你在 state 中定义的每个变量都是 store 状态的一部分。这些变量的值共同构成了 store 的当前状态，
  // 当这些变量的值发生变化时，store 的状态也随之改变，
  // 并且使用这些状态的组件会自动更新。
  state: () => ({
    /** 访问令牌（内存存储，页面刷新/关闭后丢失，防御XSS） */
    accessToken: null as string | null, //前面的null是默认值，后面的string | null是类型

    /** 标记是否正在刷新令牌（防止并发刷新请求） */
    isRefreshing: false,

    /** 等待令牌刷新的请求队列（刷新成功后重试） */ 
    // 我可能在同一时间点发起多个请求，这些请求都发现令牌过期了
    // 那么这些请求都需要等待令牌刷新完成之后才能继续
    // 这个队列就是用来存储这些等待的请求的
    refreshQueue: [] as ((token: string) => void)[] //这里存放的是函数，函数的参数是token类型是string，返回值是void
  }),
  //actions里面存储的是方法 我可以在方法中定义一些逻辑 
  // 这些逻辑就是我可以在组件中调用的方法
  actions: {
    /**
     * 保存登录状态（登录成功后调用）
     * @param token 后端返回的访问令牌
     * @param userInfo 后端返回的用户信息
     */
    setAuth(token:any) {
      if (!!token || !!token.accessToken) {
      this.accessToken = token.accessToken
      }
      // 无需手动保存刷新令牌：后端已通过 Set-Cookie 存入 HttpOnly Cookie
    },

    /**
     * 清除登录状态（登出/令牌过期时调用）
     */
    clearAuth() {
      this.accessToken = null
      this.isRefreshing = false
      this.refreshQueue = [] // 清空等待队列
    },

    /**
     * 刷新访问令牌（核心：依赖 HttpOnly Cookie 中的 refresh_token）
     * @returns 新的访问令牌
     */
    async refreshToken(): Promise<string> {
      // 1. 若正在刷新，将当前请求加入队列等待结果
      // 2. 对于我的Promise他的构造函数可以只传入一个resolve参数 在我的内部如果调用了resolve函数
      // 那么这个Promise就会变成成功态，成功态的值就是resolve函数的参数
      //如果我不想马上执行这个resolve函数 我可以把这个resolve函数存起来 等待某个时机再调用
      // 这个时机就是刷新令牌成功之后
      // 这个队列的作用就是存储所有等待刷新令牌结果的请求的resolve函数
      if (this.isRefreshing) {
        // Promise 的创建方式主要包括：
//  1.
//    基本构造函数 ： new Promise((resolve, reject) => {...}) - 最灵活的创建方式
- 1.
// 参数传递 ：可以只传递 resolve 参数，语法上合法，但根据场景决定是否需要 reject
// - 2.
// 状态流转 ：
// - Promise 初始状态为 pending（等待中）
// - 调用 resolve(value) → 状态变为 fulfilled（已成功），值为 value
// - 调用 reject(reason) → 状态变为 rejected（已失败），原因是 reason
// - 抛出异常 → 状态变为 rejected（已失败），原因是异常对象
// - 状态一旦改变，就不可再变（不可逆性）
// - 3.
// 代码执行流程 ：
// - Promise 构造函数的执行器函数会 立即同步执行
// - 不调用 resolve / reject 不会阻塞后续代码执行
// - 但会导致 Promise 一直处于 pending 状态，相关回调不会执行
//  2.
//    静态方法创建 ：
//    - Promise.resolve(value) - 创建已成功的 Promise
//    - Promise.reject(error) - 创建已失败的 Promise
//  3.
//    async/await 隐式创建 ： async 函数自动返回 Promise
        return new Promise((resolve) => {
          this.refreshQueue.push(resolve) // 刷新成功后调用 resolve 传递新令牌
        })
      }

      try {
        // 2. 标记开始刷新
        this.isRefreshing = true

        // 3. 调用后端刷新令牌接口
        // 注意：refresh_token 存储在 HttpOnly Cookie 中，浏览器会自动携带，无需前端手动传递
        const res = await post<RefreshTokenResponse>('/refreshToken', {}, {
          showLoading: false,// 刷新令牌不显示加载动画，优化体验
        } as RequestConfig)

        const newAccessToken = res.accessToken
        if (!newAccessToken) {
          throw new Error('刷新令牌失败：未返回新访问令牌')
        }
//在我的store内部可以使用this来访问我的state，getters，actions
        // 4. 更新本地访问令牌
        this.accessToken = newAccessToken

        // 5. 重试队列中所有等待的请求（传递新令牌）
        //这里就是将我之前存储的所有的Promise的resolve函数都执行一遍并且把我的新的token传递进去
        this.refreshQueue.forEach((resolve) => resolve(newAccessToken))
        //使用完之后最好清空队列可以方便我的垃圾回收机制回收
        this.refreshQueue = [] // 清空队列

        // 6. 返回新令牌（供当前请求重试）
        //无论 async 函数内部 return 什么值（原始类型如 string/number，还是对象），
        // 这个值都会被自动包裹成 Promise.resolve(返回值)。
        return newAccessToken
      } catch (error: any) {
        // 7. 刷新失败（如 refresh_token 过期/无效）
        this.clearAuth() // 清空所有状态，避免状态不一致
        throw error // 抛出错误，让 Axios 拦截器处理跳转登录
      } finally {
        // 8. 标记刷新结束
        //注意最后一定要将我的这个状态值恢复
        this.isRefreshing = false
      }
    },

    /**
     * 登出操作（调用后端接口清除 refresh_token）
     */
    async logout() {
      try {
        // 调用后端登出接口：清除服务器端的 refresh_token 状态
        await post('/logout', {}, {
          ignoreError: true // 忽略登出失败的错误提示（如网络波动）
        } as RequestConfig)
      } finally {
        // 无论后端是否成功，都清空前端状态
        this.clearAuth()
        ElMessage.success('已成功登出')
      }
    }
  },

  // 可选：定义 getter 简化状态访问（如判断是否登录）
  getters: {
    /** 是否已登录（根据 accessToken 是否存在判断） */
    //(state) => !!state.accessToken 这段代码的意思是：
    // 只要 state.accessToken 有值（即不是 null/undefined/空字符串），
    // 就返回 true，表示“已登录”；否则返回 false，表示“未登录”。
    //!! 是 JavaScript 的双重取反操作，可以把任意值转为对应的布尔值。
    //也就是只要不是 null/undefined/空字符串 就是true
    isLoggedIn: (state) => !!state.accessToken,
  }
})